﻿/*
 * LdDistinguishedName
 * 
 * initial implementation : 26.6.2013
 *
 * Copyright (c) Adam Sida, asida@centrum.cz.
 * All rights reserved.
 * 
 * for license see cz.uhul.DSMLv2.txt
 */
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;



namespace cz.uhul.DSMLv2
{



  /// <summary>
  /// represents Lightweight Directory entry Distinguished Name as object
  /// with some syntax. sugar
  /// </summary>
  public class LdDistinguishedName
  {

    private static Regex _dnLongRegex;
    private static Regex _dnShortRegex;

    static LdDistinguishedName()
    {
      Regex.CacheSize = 100;
      RegexOptions rgOpt = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture | RegexOptions.Multiline;

      //DN expression regex pattern
      //string longPattern = @"^(?<rdn>\w+[=]{1}[^,]*),(?<path>(?<rdn>\w+[=]{1}[^,]*)(,(?<rdn>\w+[=]{1}[^,]*))*)";
      //string shortPattern = @"^(?<rdn>\w+[=]{1}[^,]*)";

      string longPattern = @"^\s?(?<rdn>\w[^=]*[=]{1}[^,]*),(?<path>\s?(?<rdn>\w[^=]*[=]{1}[^,]*)(,\s?(?<rdn>\w[^=]*[=]{1}[^,]*))*)";
      string shortPattern = @"^\s?(?<rdn>\w[^=]*[=]{1}[^,]*)";

      _dnLongRegex = new Regex(longPattern, rgOpt);
      _dnShortRegex = new Regex(shortPattern, rgOpt);
      
    }




    protected LdDistinguishedName()
    { }





    /// <summary>
    /// returns Relative distinguished name, last part of DN, eg. cn=asida,cn=personal,cn=hotel,c=cz  , returns cn=asida
    /// </summary>
    public string RDN { get; protected set; }





    /// <summary>
    /// returns Relative distinguished name value part only, eg. cn=asida, returns asida
    /// </summary>
    public string RDNValue { get; protected set; }





    /// <summary>
    /// returns superior path of Distinguished name (parent node)
    /// </summary>
    public string SuperiorPath { get; protected set; }





    /// <summary>
    /// returns <c>List<string></c> of relative distinguished names
    /// </summary>
    public List<string> AllDn { get; protected set; }




    /// <summary>
    /// get parent from DN path hierarchy as another DistinguishedName instance
    /// </summary>
    public LdDistinguishedName Parent
    {
      get 
      {
        LdDistinguishedName tmp;
        LdDistinguishedName.TryParse(this.SuperiorPath, out tmp);
        return tmp;
      }
    }





    /// <summary>
    /// parses input string as DN
    /// </summary>
    /// <param name="dn">input <c>string</c> with distinguished name </param>
    /// <returns><c>LdDistinguishedName</c>instance</returns>
    /// <exception cref="ArgumentNullException">when input string is null</exception>
    /// <exception cref="ArgumentException">when input cannot parse as DN</exception>
    public static LdDistinguishedName Parse(string dn)
    {

      string[] compnt;
      string sep = "";
      bool longRegex;

      //if(string.IsNullOrEmpty(dn))
      if (dn == null)
        //throw new ArgumentNullException("null or empty input is not valid DN");
        dn = "";

      LdDistinguishedName tmp = new LdDistinguishedName();
      //tmp.value = dn; 
      tmp.AllDn = new List<string>();

      if (dn == "")
      {
        tmp.SuperiorPath = "";
        tmp.value = "";
        tmp.RDN = "";
        tmp.RDNValue = "";
        return tmp;
      }

      Match m = _dnLongRegex.Match(dn);

      if (m.Success)
        //tmp.SuperiorPath = m.Groups["path"].Value;
        longRegex = true;

      else
      {
        //try short regex
        m = _dnShortRegex.Match(dn);

        if(!m.Success)
          throw new ArgumentException("input dn '" + dn + "' is not valid DN");

        //when short regex used, SuperiorPath is empty
        tmp.SuperiorPath = "";
        longRegex = false;
      }

      //tmp.RDN = m.Groups["rdn"].Captures[0].Value;
      //tmp.RDNValue = m.Groups["rdn"].Captures[0].Value.Split('=')[1].Trim();
      tmp.RDN = _normalizeComponent(m.Groups["rdn"].Captures[0].Value);
      tmp.RDNValue = tmp.RDN.Split('=')[1];

      foreach (Capture c in m.Groups["rdn"].Captures)
      {
        compnt = c.Value.Split('=');
        tmp.AllDn.Add(compnt[0].Trim() + "=" + compnt[1].Trim());
      }

      foreach (string v in tmp.AllDn)
      {
        compnt = v.Split('=');
        tmp.value += sep + compnt[0].Trim() + "=" + compnt[1].Trim();
        sep = ",";
      }
      
      if (longRegex)
      {
        //when long regex used, must fill SuperiorPath
        sep = "";
        for (int i = 1; i < tmp.AllDn.Count; i++)
        {
          compnt = tmp.AllDn[i].Split('=');
          tmp.SuperiorPath += sep + compnt[0].Trim() + "=" + compnt[1].Trim();
          sep = ",";
        }
      }
      return tmp;

    }





    /// <summary>
    /// tries to parse input string as DN
    /// </summary>
    /// <param name="dn">input <c>string</c> with distinguished name </param>
    /// <param name="DistinguishedName">output parameter, <c>LdDistinguishedName</c> instance when success</param>
    /// <returns>true when successful DN parse, false otherwise</returns>
    public static bool TryParse(string dn, out LdDistinguishedName distinguishedName)
    {
      try
      {
        distinguishedName = LdDistinguishedName.Parse(dn);
        return true;
      }
      catch
      {
        distinguishedName = null;
        return false;
      }
    }




    //protected static string _normalizeComponents(LdDistinguishedName distinguishedName)
    //{
    //  string retVal = "", sep = "";
    //  string[] tmp;

    //  foreach (string v in distinguishedName.AllDn)
    //  {
    //    tmp = v.Split('=');
    //    retVal += sep + tmp[0].Trim() + "=" + tmp[1].Trim();
    //    sep = ",";
    //  }

    //  return retVal;
    //}




    protected static string _normalizeComponent(string RDN)
    {
      string[] tmp;

      tmp = RDN.Split('=');
      return tmp[0].Trim() + "=" + tmp[1].Trim();
      
    }





    public static implicit operator LdDistinguishedName(string value)
    {
      return LdDistinguishedName.Parse(value);
    }



    public static implicit operator string(LdDistinguishedName val)
    {
      return val.value;
    }



    private string value;





    public override string ToString()
    {
      return this.value;
    }



  }

}